import React, {
  createContext,
  useContext,
  useMemo,
  // useState,
  type JSX,
  type ReactNode,
} from "react";
import type { IPropChild } from "./types";

interface IStore<T> {
  key: string;
  store: T;
  setStore: (payload: Partial<T>) => void;
}
function getCxtProvider<T>(
  key: string,
  defaultValue: T,
  AppContext: React.Context<IStore<T>>
) {
  return ({ children }: IPropChild) => {
    // 返回一个方法
    // console.log("useCxtProvider-defaultValue", defaultValue);
    const [store, setStore] = React.useState(defaultValue);
    const value = useMemo(() => {
      // 这里为了解决useState的覆盖式更新，这里改为增量更新
      return {
        key,
        store,
        setStore: (payload = {}) =>
          setStore((state) => ({ ...state, ...payload })),
      };
    }, [store]); //为了提高性能，只有store变化时，useMemo才刷新

    return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
  };
}
// 工厂模式
const cxtCache: Record<string, Cxt> = {};

class Cxt<T = any> {
  defaultStore: IStore<T>;
  AppContext: React.Context<IStore<T>>;
  Provider: ({ children }: IPropChild) => JSX.Element;

  constructor(key: string, defaultValue: T) {
    this.defaultStore = {
      key,
      store: defaultValue,
      setStore: () => {},
    };
    this.AppContext = createContext(this.defaultStore);
    this.Provider = getCxtProvider(key, defaultValue, this.AppContext);
    cxtCache[key] = this;
  }
}

export function useAppContext<T>(key: string) {
  const cxt = cxtCache[key] as Cxt<T>;
  const app = useContext(cxt.AppContext);
  return {
    store: app.store,
    setStore: app.setStore,
  };
}

// 返回一个HOC高阶组件：这个高阶组件的返回值是一个组件
export function connectFactory<T>(key: string, defaultValue: T) {
  const cxt = cxtCache[key];
  let CxtProvider: Cxt<T>;
  if (cxt) {
    CxtProvider = cxt;
  } else {
    CxtProvider = new Cxt<T>(key, defaultValue);
  }
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (Child: React.FunctionComponent<any>) =>
    (props: any): ReactNode => {
      return (
        <CxtProvider.Provider>
          <Child {...props} />
        </CxtProvider.Provider>
      );
    };
}
